// 版权所有 Epic Games, Inc. 保留所有权利。

#include "Interaction/Tasks/AbilityTask_WaitForInteractableTargets.h"

#include "AbilitySystemComponent.h"
#include "Engine/World.h"
#include "GameFramework/PlayerController.h"
#include "Interaction/IInteractableTarget.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(AbilityTask_WaitForInteractableTargets)

struct FInteractionQuery;

UAbilityTask_WaitForInteractableTargets::UAbilityTask_WaitForInteractableTargets(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
}

/**
 * 执行射线检测
 * @param OutHitResult 输出命中结果
 * @param World 世界
 * @param Start 起始点
 * @param End 结束点
 * @param ProfileName 碰撞配置文件名称
 * @param Params 碰撞查询参数
 */
void UAbilityTask_WaitForInteractableTargets::LineTrace(FHitResult& OutHitResult, const UWorld* World, const FVector& Start, const FVector& End, FName ProfileName, const FCollisionQueryParams Params)
{
	check(World); // 检查世界是否有效

	OutHitResult = FHitResult(); // 初始化命中结果
	TArray<FHitResult> HitResults; // 命中结果数组
	World->LineTraceMultiByProfile(HitResults, Start, End, ProfileName, Params); // 多对象射线检测

	OutHitResult.TraceStart = Start; // 设置追踪起始点
	OutHitResult.TraceEnd = End; // 设置追踪结束点

	if (HitResults.Num() > 0) // 如果有命中结果
	{
		OutHitResult = HitResults[0]; // 取第一个命中结果
	}
}

/**
 * 使用玩家控制器进行瞄准
 * @param InSourceActor 源角色
 * @param Params 碰撞查询参数
 * @param TraceStart 追踪起始点
 * @param MaxRange 最大范围
 * @param OutTraceEnd 输出追踪结束点
 * @param bIgnorePitch 是否忽略俯仰角
 */
void UAbilityTask_WaitForInteractableTargets::AimWithPlayerController(const AActor* InSourceActor, FCollisionQueryParams Params, const FVector& TraceStart, float MaxRange, FVector& OutTraceEnd, bool bIgnorePitch) const
{
	if (!Ability) // 服务器和启动客户端 only
	{
		return;
	}

	//@TODO: 机器人？
	APlayerController* PC = Ability->GetCurrentActorInfo()->PlayerController.Get(); // 获取玩家控制器
	check(PC); // 检查玩家控制器是否有效

	FVector ViewStart; // 视角起始点
	FRotator ViewRot; // 视角旋转
	PC->GetPlayerViewPoint(ViewStart, ViewRot); // 获取玩家视角

	const FVector ViewDir = ViewRot.Vector(); // 视角方向
	FVector ViewEnd = ViewStart + (ViewDir * MaxRange); // 视角结束点

	ClipCameraRayToAbilityRange(ViewStart, ViewDir, TraceStart, MaxRange, ViewEnd); // 裁剪相机射线到能力范围

	FHitResult HitResult; // 命中结果
	LineTrace(HitResult, InSourceActor->GetWorld(), ViewStart, ViewEnd, TraceProfile.Name, Params); // 执行射线检测

	const bool bUseTraceResult = HitResult.bBlockingHit && (FVector::DistSquared(TraceStart, HitResult.Location) <= (MaxRange * MaxRange)); // 是否使用追踪结果

	const FVector AdjustedEnd = (bUseTraceResult) ? HitResult.Location : ViewEnd; // 调整后的结束点

	FVector AdjustedAimDir = (AdjustedEnd - TraceStart).GetSafeNormal(); // 调整后的瞄准方向
	if (AdjustedAimDir.IsZero()) // 如果调整后的瞄准方向为零
	{
		AdjustedAimDir = ViewDir; // 使用视角方向
	}

	if (!bTraceAffectsAimPitch && bUseTraceResult) // 如果追踪不影响瞄准俯仰角且使用追踪结果
	{
		FVector OriginalAimDir = (ViewEnd - TraceStart).GetSafeNormal(); // 原始瞄准方向

		if (!OriginalAimDir.IsZero()) // 如果原始瞄准方向不为零
		{
			// 转换为角度并使用原始俯仰角
			const FRotator OriginalAimRot = OriginalAimDir.Rotation(); // 原始瞄准旋转

			FRotator AdjustedAimRot = AdjustedAimDir.Rotation(); // 调整后的瞄准旋转
			AdjustedAimRot.Pitch = OriginalAimRot.Pitch; // 设置俯仰角为原始值

			AdjustedAimDir = AdjustedAimRot.Vector(); // 更新调整后的瞄准方向
		}
	}

	OutTraceEnd = TraceStart + (AdjustedAimDir * MaxRange); // 设置输出追踪结束点
}

/**
 * 将相机射线裁剪到能力范围
 * @param CameraLocation 相机位置
 * @param CameraDirection 相机方向
 * @param AbilityCenter 能力中心
 * @param AbilityRange 能力范围
 * @param ClippedPosition 裁剪后的位置
 * @return 是否成功裁剪
 */
bool UAbilityTask_WaitForInteractableTargets::ClipCameraRayToAbilityRange(FVector CameraLocation, FVector CameraDirection, FVector AbilityCenter, float AbilityRange, FVector& ClippedPosition)
{
	FVector CameraToCenter = AbilityCenter - CameraLocation; // 相机到中心的向量
	float DotToCenter = FVector::DotProduct(CameraToCenter, CameraDirection); // 到中心的点积
	if (DotToCenter >= 0)		// 如果失败，我们指向远离中心的方向，但我们可能在球体内并能够找到一个好的出口点。
	{
		float DistanceSquared = CameraToCenter.SizeSquared() - (DotToCenter * DotToCenter); // 距离平方
		float RadiusSquared = (AbilityRange * AbilityRange); // 半径平方
		if (DistanceSquared <= RadiusSquared) // 如果距离平方小于等于半径平方
		{
			float DistanceFromCamera = FMath::Sqrt(RadiusSquared - DistanceSquared); // 从相机起的距离
			float DistanceAlongRay = DotToCenter + DistanceFromCamera;						// 减去而不是加上将获得另一个交点
			ClippedPosition = CameraLocation + (DistanceAlongRay * CameraDirection);		// 相机瞄准点裁剪到范围球体
			return true; // 返回成功
		}
	}
	return false; // 返回失败
}

/**
 * 更新可交互选项
 * @param InteractQuery 交互查询
 * @param InteractableTargets 可交互目标数组
 */
void UAbilityTask_WaitForInteractableTargets::UpdateInteractableOptions(const FInteractionQuery& InteractQuery, const TArray<TScriptInterface<IInteractableTarget>>& InteractableTargets)
{
	TArray<FInteractionOption> NewOptions; // 新选项数组

	for (const TScriptInterface<IInteractableTarget>& InteractiveTarget : InteractableTargets) // 遍历可交互目标
	{
		TArray<FInteractionOption> TempOptions; // 临时选项数组
		FInteractionOptionBuilder InteractionBuilder(InteractiveTarget, TempOptions); // 交互选项构建器
		InteractiveTarget->GatherInteractionOptions(InteractQuery, InteractionBuilder); // 收集交互选项

		for (FInteractionOption& Option : TempOptions) // 遍历临时选项
		{
			FGameplayAbilitySpec* InteractionAbilitySpec = nullptr; // 交互能力规格

			// 如果有句柄和目标能力系统，我们在目标上触发能力。
			if (Option.TargetAbilitySystem && Option.TargetInteractionAbilityHandle.IsValid()) // 如果目标能力系统存在且目标交互能力句柄有效
			{
				// 查找规格
				InteractionAbilitySpec = Option.TargetAbilitySystem->FindAbilitySpecFromHandle(Option.TargetInteractionAbilityHandle); // 从句柄查找能力规格
			}
			// 如果有交互能力，那么我们在自己身上激活它。
			else if (Option.InteractionAbilityToGrant) // 如果有要授予的交互能力
			{
				// 查找规格
				InteractionAbilitySpec = AbilitySystemComponent->FindAbilitySpecFromClass(Option.InteractionAbilityToGrant); // 从类查找能力规格

				if (InteractionAbilitySpec) // 如果找到能力规格
				{
					// 更新选项
					Option.TargetAbilitySystem = AbilitySystemComponent.Get(); // 设置目标能力系统
					Option.TargetInteractionAbilityHandle = InteractionAbilitySpec->Handle; // 设置目标交互能力句柄
				}
			}

			if (InteractionAbilitySpec) // 如果交互能力规格存在
			{
				// 过滤掉任何我们现在因某种原因无法激活的选项。
				if (InteractionAbilitySpec->Ability->CanActivateAbility(InteractionAbilitySpec->Handle, AbilitySystemComponent->AbilityActorInfo.Get())) // 如果能力可以激活
				{
					NewOptions.Add(Option); // 添加到新选项
				}
			}
		}
	}

	bool bOptionsChanged = false; // 选项是否改变
	if (NewOptions.Num() == CurrentOptions.Num()) // 如果新选项数量等于当前选项数量
	{
		NewOptions.Sort(); // 排序新选项

		for (int OptionIndex = 0; OptionIndex < NewOptions.Num(); OptionIndex++) // 遍历新选项
		{
			const FInteractionOption& NewOption = NewOptions[OptionIndex]; // 新选项
			const FInteractionOption& CurrentOption = CurrentOptions[OptionIndex]; // 当前选项

			if (NewOption != CurrentOption) // 如果选项不同
			{
				bOptionsChanged = true; // 设置选项改变
				break; // 跳出循环
			}
		}
	}
	else
	{
		bOptionsChanged = true; // 设置选项改变
	}

	if (bOptionsChanged) // 如果选项改变
	{
		CurrentOptions = NewOptions; // 更新当前选项
		InteractableObjectsChanged.Broadcast(CurrentOptions); // 广播可交互对象改变事件
	}
}